/*
 * Copyright 2007-2009 Perttu "celeron55" Ahola <celeron55@gmail.com>
 *
 * This file is part of jukuMagic.
 *
 * jukuMagic is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jukuMagic is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jukuMagic.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "muut.h"
//#include "commonfunctions.h"
#include <stdlib.h>
#include <assert.h>
//#include <algorithm>
#include <stdio.h>

/*
 * kyt-tiedoston formaatti (ks. koekytkis2.kyt)
 * -------------------------
 * PART: <osan nimi>, <jalkajärjestyksen tyyppi>, <jalkojen numerot>
 * PART: LM324, DIP 4 7, 0=jalannimi 1 2 3=moi 4 5 6 7 8 9 10 11 12 13;
 * PART: R47K, wirepart, 14=+ 15=-;
 * PART: R47K, row 3, 15 16 17;
 * CON: 0, 14;
 * CON: 4, 15;
 * -----------------
 * 
 * TODO:
 * - Monijalkaiset wirepartsit?
 * - guihin epä-statikkien osien lukitseminen paikoilleen (yksitellen)
 * - guihin joku osan oikealla klikkauksella tuleva säätövalikko?
 * - "Set every part static"
 * - staticiksi asettaessa piirrä viivoilla osan yhteydet
 * - nfc-määrä footin ominaisuudeksi?
 * - OptimizeChunkOrders osaamaan vaihdella järjestyksiä vaikka olisi paikallaan pysyviä jalkoja seassa
 * - eaglen netlist-formaatti
 * - WireParttien pituudentarkistus takaisin
 * - WireParttien törmäystarkistus
 * - johtoja ja johto-osia lisätessä pitää voida lisätä vaikka ei saisi suoraa yhteyttä (jalan viereiset kohdat blokattu)
 *
 */

bool debugmessages_on = false;

///////////////////

Coord::Coord()
{
	exist = true;
	x = 0;
	y = 0;
}

Coord::Coord(int x_, int y_)
{
	exist = true;
	x = x_;
	y = y_;
}

bool Coord::operator == (Coord c)
{
	if(x==c.x && y==c.y && exist && c.exist) return true;
	return false;
}

bool Coord::operator != (Coord c)
{
	if(x!=c.x || y!=c.y) return true;
	return false;
}

Coord Coord::operator + (Coord c)
{
	Coord c2;
	c2.x = x + c.x;
	c2.y = y + c.y;
	return c2;
}

Coord MakeCoord(unsigned int x, unsigned int y){
	Coord c;
	c.x = x;
	c.y = y;
	return c;
}

Coord MakeInexistentCoord(){
	Coord c;
	c.exist = false;
	return c;
}

double Distance(Coord c1, Coord c2)
{
	double d;
	if(!c1.exist || !c2.exist) return 0.0;
	d = sqrt(pow((double)c2.x-(double)c1.x, 2) + pow((double)c2.y-(double)c1.y, 2));
	//if(d <= 6){
		//unsigned int xchange = (c1.x>c2.x)?(c1.x-c2.x):(c2.x-c1.x);
		//unsigned int ychange = (c1.y>c2.y)?(c1.y-c2.y):(c2.y-c1.y);
		//if(xchange==0) d /= 2;
		//if(ychange==0) d /= 2;
	//}
	//cout<<c1.x<<","<<c1.y<<" "<<c2.x<<","<<c2.y<<" d="<<d<<endl;
	return d;
}

double Distance(Coord c1, vector<Coord> c2s)
{
	if(c2s.empty()){ cout<<"Distance(): ERROR: c2s is empty!"<<endl; return -1; }
	if(c2s.size()==1) return Distance(c1, c2s[0]);
	double d = 0;
	for(unsigned int i=0; i<c2s.size(); i++){
		d += Distance(c1, c2s[i]);
		//d +=sqrt(pow((double)c2s[i].x-(double)c1.x, 2) + pow((double)c2s[i].y-(double)c1.y, 2));
	}
	//cout<<c1.x<<","<<c1.y<<" "<<c2.x<<","<<c2.y<<" d="<<d<<endl;
	return d;
}

string Coord::String()
{
	if(!exist) return "[!exist]";
	char a[30];
	sprintf(a, "(%i,%i)", x, y);
	string b;
	b = a;
	return b;
}

void DumpCoords(vector<Coord> cs)
{
	if(cs.size()==0){
		cout<<"[none]";
		return;
	}
	for(unsigned int i=0; i<cs.size(); i++){
		cout<<cs[i].String();
		if(i!=cs.size()-1) cout<<",";
	}
}

void Line(int x0, int y0, int x1, int y1, vector<Coord> &cs)
{
	int dy = y1 - y0;
	int dx = x1 - x0;
	int stepx, stepy;

	if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }
	if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }
	dy <<= 1;                                                  // dy is now 2*dy
	dx <<= 1;                                                  // dx is now 2*dx

	Coord c(x0,y0);
	cs.push_back(c);
	if (dx > dy) {
		int fraction = dy - (dx >> 1);                         // same as 2*dy - dx
		while (x0 != x1) {
			if (fraction >= 0) {
				y0 += stepy;
				fraction -= dx;                                // same as fraction -= 2*dx
			}
			x0 += stepx;
			fraction += dy;                                    // same as fraction -= 2*dy
			Coord c(x0,y0);
			cs.push_back(c);
		}
	} else {
		int fraction = dx - (dy >> 1);
		while (y0 != y1) {
			if (fraction >= 0) {
				x0 += stepx;
				fraction -= dy;
			}
			y0 += stepy;
			fraction += dx;
			Coord c(x0,y0);
			cs.push_back(c);
		}
	}
}

///////////////////////

Contentblock::Contentblock()
{
	type = CUT;
	//component_blocks = false;
	partname_blocks = "";
	nfc_partnum = -1;
}

Contentblock::Contentblock(Contenttype t, int n)
{
	type = t;
	//component_blocks = false;
	partname_blocks = "";
	nfc_partnum = -1;
	num = n;
}

string Contentblock::TypeString()
{
	switch(type){
	case CUT: return "CUT";
	case HOLE: return "HOLE";
	case PFOOT: return "PFOOT";
	case WPFOOT: return "WPFOOT";
	case WIREHEAD: return "WIREHEAD";
	case OVERBOARD: return "OVERBOARD";
	default: return "[INVALID TYPE]";
	}
}

//////////////////////

Wire::Wire()
{
}

Wire::Wire(int num_)
{
	num = num_;
}

Wire::Wire(Coord c1_, Coord c2_, int num_)
{
	c1 = c1_;
	c2 = c2_;
	num = num_;
}

Coord Wire::OtherHeadCoord(Coord c_this)
{
	if(c1 == c_this){
		 return c2;
	}
	else if(c2 == c_this){
		return c1;
	}
	else return MakeInexistentCoord();
}

Coord Wire::C1()
{
	return c1;
}

Coord Wire::C2()
{
	return c2;
}

void Wire::SetCoord(Coord c1_, Coord c2_)
{
	c1 = c1_;
	c2 = c2_;
}

//////////////////////

DConn::DConn()
{
}

DConn::DConn(int num_)
{
	num = num_;
}

DConn::DConn(Coord c1_, Coord c2_, int num_)
{
	c1 = c1_;
	c2 = c2_;
	//näin vielä kun on pelkkä juovakuparoitu
	if(c1.y!=c2.y){
		c1.exist = false;
		c2.exist = false;
	}
	num = num_;
}

Coord DConn::OtherHeadCoord(Coord c_this)
{
	if(c1 == c_this){
		 return c2;
	}
	else if(c2 == c_this){
		return c1;
	}
	else return MakeInexistentCoord();
}

Coord DConn::C1()
{
	return c1;
}

Coord DConn::C2()
{
	return c2;
}

void DConn::SetCoord(Coord c1_, Coord c2_)
{
	c1 = c1_;
	c2 = c2_;
	//näin vielä kun on pelkkä juovakuparoitu
	if(c1.y!=c2.y){
		c1.exist = false;
		c2.exist = false;
	}
}

void DConn::GetNodes(vector<Coord> &nodes)
{
	//näin vielä
	unsigned int y=c1.y;
	unsigned int start = c1.x < c2.x ? c1.x : c2.x;
	unsigned int stop = c1.x > c2.x ? c1.x : c2.x;
	for(unsigned int x = start; x<=stop; x++){
		nodes.push_back(MakeCoord(x,y));
	}
}


////////////////

Connection::Connection()
{
}

Connection::Connection(int from_, int to_)
{
	from = from_;
	to = to_;
}

/////////////////

Footprint::Footprint()
{
	width = 0;
	height = 0;
	maxfootnum = 0;
	rotate = false;
}

void Footprint::Allocate(unsigned int w, unsigned int h)
{
	cout<<"Footprint::Allocate(w="<<w<<", h="<<h<<")"<<endl;
	contentblocks.clear();
	rotatedcontentblocks.clear();
	Contentblock b;
	vector<Contentblock> blankrow;
	for(unsigned int i=0; i<w; i++) blankrow.push_back(b);
	for(unsigned int y=0; y<h; y++){
		contentblocks.push_back(blankrow);
		rotatedcontentblocks.push_back(blankrow);
	}
	width = w;
	height = h;
}

bool Footprint::AddFoot(unsigned int x, unsigned int y, int number, string name)
{
	//cout<<"    adding foot x="<<x<<" y="<<y<<" number="<<number<<endl;
	if(x>=width || y>=height){
		cout<<"Footprint::AddFoot(): ERROR: ("<<x<<","<<y<<") out of area"<<endl;
		return false;
	}
	
	unsigned int rx = width-1-x;
	unsigned int ry = height-1-y;

	Foot foot;
	foot.number = number;
	foot.name = name;
	foot.x = x;
	foot.y = y;
	foots.push_back(foot);
	
	foot.x = rx;
	foot.y = ry;
	rotatedfoots.push_back(foot);

	contentblocks[y][x].type = PFOOT;
	contentblocks[y][x].num = number;

	rotatedcontentblocks[ry][rx].type = PFOOT;
	rotatedcontentblocks[ry][rx].num = number;
	
	//jalan kohta on myös blokattu
	//contentblocks[y][x].component_blocks = true;
	contentblocks[y][x].partname_blocks = "this";
	rotatedcontentblocks[ry][rx].partname_blocks = "this";

	if(number>maxfootnum) maxfootnum = number;
	//cout<<"width="<<width<<" height="<<height<<endl;
	return true;
}

bool Footprint::AddComponentBlock(unsigned int x, unsigned int y)
{
	if(x>=width || y>=height){
		cout<<"Footprint::AddComponentBlock(): ERROR: ("<<x<<","<<y<<") out of area"<<endl;
		return false;
	}
	//contentblocks[y][x].component_blocks = true;
	contentblocks[y][x].partname_blocks = "this";
	unsigned int rx = width-1-x;
	unsigned int ry = height-1-y;
	rotatedcontentblocks[ry][rx].partname_blocks = "this";
	return true;
}

unsigned int Footprint::Width()
{
	return width;
}
unsigned int Footprint::Height()
{
	return height;
}
int Footprint::FootCount()
{
	return foots.size();
}
int Footprint::MaxFootNum()
{
	return maxfootnum;
}
Foot Footprint::GetFoot(int i)
{
	if(rotate) return rotatedfoots[i];
	else return foots[i];
}

Foot Footprint::GetFootByNumber(int n)
{
	Foot f;
	for(unsigned int i=0; i<foots.size(); i++){
		if(foots[i].number==n){
			if(rotate) return rotatedfoots[i];
			else return foots[i];
		}
	}
	f.number = -1;
	return f;
}

vector<Foot> Footprint::Foots()
{
	if(rotate) return rotatedfoots;
	else return foots;
}

vector< vector<Contentblock> > Footprint::Contentblocks()
{
	if(rotate) return rotatedcontentblocks;
	else return contentblocks;
	/*vector< vector<Contentblock> > blocks;
	vector<Contentblock> blockrow;
	for(unsigned int i=0; i<Width(); i++){
		Contentblock b;
		//b.type = COMPONENT;
		b.type = CUT;
		b.component_blocks = true;
		blockrow.push_back(b);
	}
	for(unsigned int i=0; i<Height(); i++){
		blocks.push_back(blockrow);
	}
	for(int i=0; i<FootCount(); i++){
		Foot f = GetFoot(i);
		//cout<<"adding foot "<<i<<" x="<<f.x<<" y="<<f.y<<" to blocks"<<endl;
		Contentblock b;
		b.type = PFOOT;
		b.num = f.number;
		blocks[f.y][f.x] = b;
	}
	return blocks;*/
}

bool Footprint::HasAdjacentFeet()
{
	if(width == 1) return false;
	for(unsigned int y=0; y<height; y++){
		bool found = false;
		for(unsigned int x=0; x<width; x++){
			if(contentblocks[y][x].type == PFOOT){
				if(found) return true;
				found = true;
			}
		}
	}
	return false;
}

void Footprint::SetRotate(bool r)
{
	rotate = r;
}

bool Footprint::GetRotate()
{
	return rotate;
}

////////////////////////////////////////////////////////////////////////////

Part::Part()
{
	coords.exist = false;
	locked = false;
}

void Part::SetCoord(Coord coords_)
{
	assert(locked == false);
	coords = coords_;
}

Coord Part::GetCoord()
{
	return coords;
}

void Part::SetName(string n)
{
	name = n;
}

string Part::Name()
{
	return name;
}

void Part::SetFootprint(Footprint fp)
{
	footprint = fp;
}

/*Footprint Part::GetFootprint()
{
	return footprint;
}*/

Footprint * Part::GetFootprint()
{
	return &footprint;
}

void Part::SetLocked(bool locked_)
{
	locked = locked_;
}
bool Part::Locked()
{
	return locked;
}

vector<Part> SortPartsByFootCount(vector<Part> parts0){
	vector<Part> parts1;
	while(!parts0.empty()){
		unsigned int largestcount = 0;
		int num = -1;
		for(unsigned int i=0; i<parts0.size(); i++){
			unsigned int fc = parts0[i].GetFootprint()->FootCount();
			if(fc > largestcount || num == -1){
				largestcount = fc;
				num = i;
			}
		}
		cout<<"SortPartsByFootCount(): "<<parts0[num].Name()<<endl;
		parts1.push_back(parts0[num]);
		parts0.erase(parts0.begin()+num);
	}
	return parts1;
}

WirePart::WirePart()
{
	maxfootnum = 0;
	c1.exist = false;
	c2.exist = false;
}

WirePart::WirePart(int l, int f1, int f2)
{
	SetLength(l);
	SetFootNums(f1, f2);
}

void WirePart::SetCoord(Coord c1_, Coord c2_)
{
	c1 = c1_;
	c2 = c2_;
}

Coord WirePart::C1()
{
	return c1;
}

Coord WirePart::C2()
{
	return c2;
}

Coord WirePart::OtherHeadCoord(Coord c_this)
{
	if(c1 == c_this){
		 return c2;
	}
	else if(c2 == c_this){
		return c1;
	}
	else return MakeInexistentCoord();
}

void WirePart::SetLength(int l)
{
	length = l;
}

void WirePart::SetFootNums(int n1, int n2)
{
	footn1 = n1;
	footn2 = n2;
	maxfootnum = (n1>n2) ? n1 : n2;
}

void WirePart::SetFootNames(string f1n, string f2n)
{
	f1name = f1n;
	f2name = f2n;
}

void WirePart::SetName(string n)
{
	name = n;
}

string WirePart::Name()
{
	return name;
}

int WirePart::Length()
{
	return length;
}

int WirePart::FootNum1()
{
	return footn1;
}

int WirePart::FootNum2()
{
	return footn2;
}

string WirePart::FootName1()
{
	return f1name;
}

string WirePart::FootName2()
{
	return f2name;
}

int WirePart::MaxFootNum()
{
	return maxfootnum;
}

bool WirePart::HalfDefined()
{
	if((c1.exist && !c2.exist) || (!c1.exist && c2.exist)) return true;
	return false;
}


